package com.ihr360.payroll.service.salaryreport.impl;

import com.google.common.collect.Lists;
import com.ihr360.authority.sharing.model.vo.SimpleAuthUserResourceVo;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.DateUtils;
import com.ihr360.payroll.dao.ActivePayPeriodRepository;
import com.ihr360.payroll.dao.HistoryPayPeriodRepository;
import com.ihr360.payroll.dao.HistorySalaryPlanRepository;
import com.ihr360.payroll.dao.PayPeriodTermRepository;
import com.ihr360.payroll.entity.task.PayPeriodTermEntity;
import com.ihr360.payroll.model.dto.attendance.plan.AttendancePlan;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlanStaff;
import com.ihr360.payroll.model.dto.salary.report.HistoryPayPeriod;
import com.ihr360.payroll.model.dto.salary.report.HistorySalaryPlan;
import com.ihr360.payroll.model.dto.salary.report.MergeSalaryReport;
import com.ihr360.payroll.model.dto.salary.report.MergeSalaryReportTermSalary;
import com.ihr360.payroll.model.dto.salary.report.embedded.AttendancePlanSnapshot;
import com.ihr360.payroll.model.dto.salary.report.embedded.PayrollConfigSnapshot;
import com.ihr360.payroll.model.dto.salary.report.embedded.SalaryFieldSnapshot;
import com.ihr360.payroll.model.dto.salary.report.embedded.SalaryPlanStaffSnapshot;
import com.ihr360.payroll.model.dto.salary.report.embedded.SalaryStaffSnapshot;
import com.ihr360.payroll.model.dto.salary.report.embedded.attendancePlan.EnrollAdjustRuleSnapShot;
import com.ihr360.payroll.model.dto.salary.task.period.ActivePayPeriod;
import com.ihr360.payroll.model.dto.salary.task.period.PayPeriodTerm;
import com.ihr360.payroll.response.salaryreport.SalaryReportMonthResponse;
import com.ihr360.payroll.service.payslip.result.PayslipResultService;
import com.ihr360.payroll.service.salaryreport.HistoryPayPeriodService;
import com.ihr360.payroll.service.salaryreport.MergeSalaryReportService;
import com.ihr360.payroll.service.salaryreport.MergeSalaryReportTermSalaryService;
import com.ihr360.payroll.service.salaryreport.job.MergeSalaryReportDataJob;
import com.ihr360.payroll.type.PeriodState;
import com.ihr360.payroll.type.ReportState;
import com.ihr360.rest.reposity.SimpleRestReposity;
import com.ihr360.rest.service.AbstractSimpleRestServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by Eric on 2018/4/11/011.
 */
@Service
public class HistoryPayPeriodServiceImpl extends AbstractSimpleRestServiceImpl<HistoryPayPeriod, Long> implements HistoryPayPeriodService {

    private static Logger logger = LoggerFactory.getLogger(HistoryPayPeriodServiceImpl.class);

    @Autowired
    private HistoryPayPeriodRepository historyPayPeriodRepository;

    @Autowired
    private HistorySalaryPlanRepository historySalaryPlanRepository;

    @Autowired
    private ActivePayPeriodRepository activePayPeriodRepository;

    @Autowired
    private MergeSalaryReportService mergeSalaryReportService;

    @Autowired
    private PayPeriodTermRepository payPeriodTermRepository;

    @Autowired
    ActivePayPeriodRepository payPeriodRepository;

    @Autowired
    private MergeSalaryReportDataJob mergeSalaryReportDataJob;

    @Autowired
    private EntityManager entityManager;


    @Autowired
    private MergeSalaryReportTermSalaryService mergeSalaryReportTermSalaryService;
    @Autowired
    private PayslipResultService payslipResultService;

    @Override
    public SimpleRestReposity<HistoryPayPeriod, Long> getSimpleRestReposity() {
        return historyPayPeriodRepository;
    }

    @Override
    public List<PayPeriodTermEntity> queryTermList(String companyId, Long salaryPlanId, Date date) {
        List<PayPeriodTermEntity> list = Lists.newArrayList();
        int year = Integer.parseInt(DateUtils.formatDateStr(date, DateUtils.STANDARD_DATE_FORMAT_Y));
        int month = Integer.parseInt(DateUtils.formatDateStr(date, DateUtils.STANDARD_DATE_FORMAT_M));
        HistoryPayPeriod historyPayPeriod = historyPayPeriodRepository.findByCompanyIdAndSalaryPlanSalaryPlanIdAndYearAndMonth(companyId, salaryPlanId, year, month);
        if (historyPayPeriod == null || CollectionUtils.isEmpty(historyPayPeriod.getTerms())) {
            return list;
        }
        for (PayPeriodTerm term : historyPayPeriod.getTerms()) {
            PayPeriodTermEntity termEntity = new PayPeriodTermEntity();
            BeanUtils.copyProperties(term, termEntity);
            list.add(termEntity);
        }
        return list;
    }

    @Override
    public List<Integer> querySalaryReportYearListList(String companyId, Long salaryPlanId) {
        List<Integer> list = Lists.newArrayList();
        List<HistoryPayPeriod> historyPayPeriodList = historyPayPeriodRepository.findByCompanyIdAndSalaryPlanSalaryPlanIdOrderByYear(companyId, salaryPlanId);
        if (CollectionUtils.isEmpty(historyPayPeriodList)) {
            return list;
        }
        historyPayPeriodList.forEach(historyPayPeriod -> {
            Integer year = historyPayPeriod.getYear();
            if (!list.contains(year)) {
                list.add(year);
            }
        });
        return list;
    }

    @Override
    public List<SalaryReportMonthResponse> querySalaryReportMonthList(String companyId, Long salaryPlanId, int year) {
        List<SalaryReportMonthResponse> list = Lists.newArrayList();
        List<HistoryPayPeriod> historyPayPeriodList = historyPayPeriodRepository.findByCompanyIdAndSalaryPlanSalaryPlanIdAndYearOrderByMonth(companyId, salaryPlanId, year);
        if (CollectionUtils.isEmpty(historyPayPeriodList)) {
            return list;
        }
        historyPayPeriodList.forEach(historyPayPeriod -> {
            SalaryReportMonthResponse reportMonthResponse = new SalaryReportMonthResponse();
            if (CollectionUtils.isNotEmpty(historyPayPeriod.getTerms())) {
                reportMonthResponse.setMonth(historyPayPeriod.getMonth());
                //默认批次
                reportMonthResponse.setTermId(historyPayPeriod.getTerms().get(0).getId());
                list.add(reportMonthResponse);
            }
        });
        return list;
    }

    @Override
    public HistoryPayPeriod getHistoryPayPeriod(String companyId, Long salaryPlanId, Date date) {
        int year = Integer.parseInt(DateUtils.formatDateStr(date, DateUtils.STANDARD_DATE_FORMAT_Y));
        int month = Integer.parseInt(DateUtils.formatDateStr(date, DateUtils.STANDARD_DATE_FORMAT_M));
        return historyPayPeriodRepository.findByCompanyIdAndSalaryPlanSalaryPlanIdAndYearAndMonth(companyId, salaryPlanId, year, month);
    }

    @Override
    public List<PayPeriodTermEntity> queryMergeTermList(String companyId, Long mergeReportId, Date date) {
        List<PayPeriodTermEntity> list = Lists.newArrayList();
        MergeSalaryReport mergeSalaryReport = mergeSalaryReportService.getOne(mergeReportId);
        if (mergeSalaryReport == null) {
            throw new Ihr360Exception(null, "合并报表不存在");
        }
        List<MergeSalaryReportTermSalary> termList = mergeSalaryReportTermSalaryService.queryTermSalaryByTermDateAndState(companyId, mergeReportId, DateUtils.dateFormat(date, DateUtils.STANDARD_DATE_FORMAT_YMD), ReportState.FINISHED);
        if (CollectionUtils.isEmpty(termList)) {
            return list;
        }
        List<Integer> indexList = Lists.newArrayList();
        termList.stream().filter(mergeTerm -> mergeTerm.getSalaryHeader() != null).forEach(mergeTerm -> {
            Integer index = mergeTerm.getIndexno();
            if (index != null && !indexList.contains(index)) {
                indexList.add(index);
            }
        });
        Collections.sort(indexList);
        indexList.forEach(index -> {
            PayPeriodTermEntity entity = new PayPeriodTermEntity();
            entity.setTermIndex(index);
            list.add(entity);
        });
        return list;
    }

    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void syncPayPeriod(String companyId, Long payPeriodId) {
        //查询薪酬账期
        ActivePayPeriod activePayPeriod = activePayPeriodRepository.findOne(payPeriodId);
        //关账状态下同步快照数据
        if (activePayPeriod == null) {
            throw new Ihr360Exception(null, "薪酬账期数据异常");
        }
        //如果该月该薪资方案已存在历史记录，则以最新的为准
        Long salaryPlanId = activePayPeriod.getSalaryPlan().getId();
        int year = activePayPeriod.getYear();
        int month = activePayPeriod.getMonth();
        HistoryPayPeriod existPayPeriod = historyPayPeriodRepository.findByCompanyIdAndSalaryPlanSalaryPlanIdAndYearAndMonth(companyId, salaryPlanId, year, month);
        if (existPayPeriod != null) {
            List<PayPeriodTerm> termList = existPayPeriod.getTerms();
            termList.forEach(payPeriodTerm -> payPeriodTerm.setHistoryPayPeriod(null));
            payPeriodTermRepository.save(termList);
            historyPayPeriodRepository.delete(existPayPeriod.getId());
            entityManager.flush();
        }
        HistoryPayPeriod historyPayPeriod = new HistoryPayPeriod();
        //薪酬账期数据
        historyPayPeriod.setId(payPeriodId);
        historyPayPeriod.setState(PeriodState.CLOSED);
        BeanUtils.copyProperties(activePayPeriod, historyPayPeriod);
        //薪资方案数据
        HistorySalaryPlan historySalaryPlan = new HistorySalaryPlan();
        SalaryPlan salaryPlan = activePayPeriod.getSalaryPlan();
        if (salaryPlan == null) {
            throw new Ihr360Exception(null, "薪资方案数据异常");
        }
        BeanUtils.copyProperties(salaryPlan, historySalaryPlan);
        historySalaryPlan.setCreatedAt(new Date());
        historySalaryPlan.setUpdatedAt(new Date());
        //员工
        if (CollectionUtils.isNotEmpty(salaryPlan.getStaffIdList())) {
            SalaryPlanStaffSnapshot salaryPlanStaffSnapshot = new SalaryPlanStaffSnapshot();
            List<SalaryStaffSnapshot> list = Lists.newArrayList();
            for (SalaryPlanStaff salaryPlanStaff : salaryPlan.getStaffs()) {
                SalaryStaffSnapshot salaryStaffSnapshot = new SalaryStaffSnapshot();
                BeanUtils.copyProperties(salaryPlanStaff, salaryStaffSnapshot);
                list.add(salaryStaffSnapshot);
            }
            salaryPlanStaffSnapshot.setStaffList(list);
            historySalaryPlan.setStaffs(salaryPlanStaffSnapshot);
        }
        historySalaryPlan.setSalaryPlanId(salaryPlan.getId());
        //考勤方案保存 目前只保存 入离职调整规则
        AttendancePlanSnapshot attendancePlanSnapshot = new AttendancePlanSnapshot();
        AttendancePlan attendancePlan = salaryPlan.getAttendancePlan();
        if (attendancePlan != null && attendancePlan.getEnrollAdjustRule() != null) {
            BeanUtils.copyProperties(attendancePlan, attendancePlanSnapshot);
            EnrollAdjustRuleSnapShot enrollAdjustRuleSnapShot = new EnrollAdjustRuleSnapShot();
            BeanUtils.copyProperties(attendancePlan.getEnrollAdjustRule(), enrollAdjustRuleSnapShot);
            attendancePlanSnapshot.setEnrollAdjustRuleSnapShot(enrollAdjustRuleSnapShot);
            historySalaryPlan.setAttendancePlan(attendancePlanSnapshot);
        }

        //工资表设置
        PayrollConfigSnapshot payrollConfigSnapshot = new PayrollConfigSnapshot();
        BeanUtils.copyProperties(salaryPlan.getPayrollConfig(), payrollConfigSnapshot);
        //工资表字段
        List<SalaryField> salaryFieldList = salaryPlan.getPayrollConfig().getFields();
        List<SalaryFieldSnapshot> salaryFieldSnapshotList = Lists.newArrayList();
        for (SalaryField salaryField : salaryFieldList) {
            SalaryFieldSnapshot salaryFieldSnapshot = new SalaryFieldSnapshot();
            BeanUtils.copyProperties(salaryField, salaryFieldSnapshot);
            if (salaryField.getValueMeta() != null) {
                BeanUtils.copyProperties(salaryField.getValueMeta(), salaryFieldSnapshot);
            }
            salaryFieldSnapshotList.add(salaryFieldSnapshot);
        }
        payrollConfigSnapshot.setFields(salaryFieldSnapshotList);
        historySalaryPlan.setPayrollConfig(payrollConfigSnapshot);
        //保存薪资方案快照数据
        historySalaryPlan = historySalaryPlanRepository.save(historySalaryPlan);
        //保存薪酬账期快照数据
        historyPayPeriod.setSalaryPlan(historySalaryPlan);
        historyPayPeriod.setCreatedAt(new Date());
        historyPayPeriod.setUpdatedAt(new Date());
        List<PayPeriodTerm> terms = Lists.newArrayList();
        List<PayPeriodTerm> periodTermList = activePayPeriod.getTerms();
        for (PayPeriodTerm payPeriodTerm : periodTermList) {
            payPeriodTerm.setHistoryPayPeriod(historyPayPeriod);
            terms.add(payPeriodTerm);
        }
        historyPayPeriod.setTerms(terms);
        historyPayPeriod = historyPayPeriodRepository.save(historyPayPeriod);
        //复制保存activePayPeriod的salaryPlan分享权限设置
        List<SimpleAuthUserResourceVo> salaryPlanAuthUserList = authUserResourceService.getResourceUsers(SalaryPlan.class.getSimpleName(), activePayPeriod.getSalaryPlan().getId().toString());

        if (CollectionUtils.isNotEmpty(salaryPlanAuthUserList)) {
            //历史方案权限
            List<String> salaryPlanUserIdList = salaryPlanAuthUserList.stream().map(SimpleAuthUserResourceVo::getUserId).collect(Collectors.toList());
            authUserResourceService.addUserResource(companyId, HistorySalaryPlan.class.getSimpleName(), salaryPlanUserIdList, historyPayPeriod.getSalaryPlan().getId().toString());
        }
        // 生成工资单结果
        payslipResultService.generatePayslipResult(historyPayPeriod, activePayPeriod);
        //同步完成后删除活动账期记录
        closePayPeriodDelete(activePayPeriod);
        //开始计算合并报表
        mergeSalaryReportDataJob.calMergeReport(companyId, historyPayPeriod);
        /*if (entityManager.isJoinedToTransaction()) {
            HistoryPayPeriod finalHistoryPayPeriod = historyPayPeriod;
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    logger.debug("start cal merge report after transaction commit.");
                    mergeSalaryReportDataJob.calMergeReport(companyId, finalHistoryPayPeriod);
                }
            });
        } else {
            logger.debug("Process message out of transaction.");
            mergeSalaryReportDataJob.calMergeReport(companyId, historyPayPeriod);
        }*/
    }

    /**
     * 关账后删除账期记录
     */
    private void closePayPeriodDelete(ActivePayPeriod payPeriod) {
        List<PayPeriodTerm> terms = payPeriod.getTerms();
        if (CollectionUtils.isNotEmpty(terms)) {
            terms.forEach(term -> term.setPayPeriod(null));
            payPeriodTermRepository.save(terms);
        }
        payPeriodRepository.delete(payPeriod.getId());
    }

    @Override
    public List<HistoryPayPeriod> queryListByCompanyIdAndIdList(String companyId, List<Long> idList) {
        return historyPayPeriodRepository.findByCompanyIdAndIdIn(companyId, idList);
    }

    @Override
    public List<HistoryPayPeriod> queryListByCompanyIdAndSalaryPlanIdAndDate(String companyId, List<Long> salaryPlanId, Date reportDate) {
        int year = Integer.parseInt(DateUtils.formatDateStr(reportDate, DateUtils.STANDARD_DATE_FORMAT_Y));
        int month = Integer.parseInt(DateUtils.formatDateStr(reportDate, DateUtils.STANDARD_DATE_FORMAT_M));
        return historyPayPeriodRepository.findByCompanyIdAndSalaryPlanSalaryPlanIdInAndYearAndMonth(companyId, salaryPlanId, year, month);
    }
}
